package com.dap.servicegovernance.trace;

import com.dap.servicegovernance.constants.TraceConstants;
import com.dap.utils.PrintArgumentUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * trace切面处理类
 */
public class TraceAOPLocal implements MethodInterceptor {
    private final static Logger LOGGER = LoggerFactory.getLogger("tracelog");

    protected final String logLevel;
    protected final boolean isLogEnabled;


    /**
     * 构造函数
     * @param logLevel 打印的日志级别
     */
    public TraceAOPLocal(String logLevel) {
        super();

        logLevel = logLevel.toLowerCase();
        // 默认为 trace
        if (!"debug".equals(logLevel) && !"info".equals(logLevel) && !"warn".equals(logLevel) && !"error".equals(logLevel))
            logLevel = "trace";

        this.logLevel = logLevel;
        switch (this.logLevel) {
            case "debug":
                isLogEnabled = LOGGER.isDebugEnabled();
                break;
            case "info":
                isLogEnabled = LOGGER.isInfoEnabled();
                break;
            case "warn":
                isLogEnabled = LOGGER.isWarnEnabled();
                break;
            case "error":
                isLogEnabled = LOGGER.isErrorEnabled();
                break;

            default:    // "trace"
                isLogEnabled = LOGGER.isTraceEnabled();
                break;
        }
    }


    @Override
    public final Object invoke(MethodInvocation invocation) throws Throwable {
        if (isLogEnabled) {
            String className = invocation.getMethod().getDeclaringClass().getName();
            String methodName = invocation.getMethod().getName();
            Object[] args = invocation.getArguments();
            long beginTime = System.nanoTime();

            // 调用前trace处理
            String parentClassName = beforeMethodInvoked(className, methodName, args);

            // 调用
            Object invokeResult = null;
            try {
                invokeResult = invocation.proceed();
            } finally {
                // 调用后trace处理
                afterMethodInvoked(className, methodName, beginTime, invokeResult, parentClassName);
            }
            return invokeResult;
        } else {
            return invocation.proceed();
        }
    }
    /**
     * tracelog 处理类
     *
     * @param jp
     * @return
     * @throws Throwable
     */
    public final Object traceLog(ProceedingJoinPoint jp) throws Throwable {
        if (isLogEnabled) {
            String className = jp.getSignature().getDeclaringTypeName();
            String methodName = jp.getSignature().getName();
            Object[] args = jp.getArgs();
            long beginTime = System.nanoTime();

            // 调用前trace处理
            String parentClassName = beforeMethodInvoked(className, methodName, args);

            // 调用
            Object invokeResult = null;
            try {
                invokeResult = jp.proceed();
            } finally {
                // 调用后trace处理
                afterMethodInvoked(className, methodName, beginTime, invokeResult, parentClassName);
            }
            return invokeResult;
        } else {
            return jp.proceed();
        }
    }


    /**
     * 方法调用前处理
     */
    protected String beforeMethodInvoked(String className, String methodName, Object[] args) {
        logBeforeMethodInvoked(className, methodName, args);
        return null;
    }
    /**
     * 方法调用后处理
     */
    protected void afterMethodInvoked(String className, String methodName, long beginTime, Object invokeResult, String parentClassName) {
        logAfterMethodInvoked(className, methodName, beginTime, invokeResult);
    }



    protected void logBeforeMethodInvoked(String className, String methodName, Object[] args) {
        // 打印参数
        final String logFormatStr = "class={},method={},input={}";
        final Object[] logParamArray = {className, methodName, PrintArgumentUtil.toJSONString(args)};
        switch (logLevel) {
            case "trace":
                LOGGER.trace(logFormatStr, logParamArray);
                break;
            case "debug":
                LOGGER.debug(logFormatStr, logParamArray);
                break;
            case "info":
                LOGGER.info(logFormatStr, logParamArray);
                break;
            case "warn":
                LOGGER.warn(logFormatStr, logParamArray);
                break;
            case "error":
                LOGGER.error(logFormatStr, logParamArray);
                break;
        }
    }

    protected void logAfterMethodInvoked(String className, String methodName, long beginTime, Object invokeResult) {
        BigDecimal costTime = new BigDecimal(System.nanoTime() - beginTime).divide(TraceConstants.ONE_MILLION_VALUE, 3, RoundingMode.HALF_UP);

        final String logFormatStr = "class={},method={},costTime={}ms,output={}";
        final Object[] logParamArray = {className, methodName, costTime, PrintArgumentUtil.toJSONString(invokeResult)};
        switch (logLevel) {
            case "trace":
                LOGGER.trace(logFormatStr, logParamArray);
                break;
            case "debug":
                LOGGER.debug(logFormatStr, logParamArray);
                break;
            case "info":
                LOGGER.info(logFormatStr, logParamArray);
                break;
            case "warn":
                LOGGER.warn(logFormatStr, logParamArray);
                break;
            case "error":
                LOGGER.error(logFormatStr, logParamArray);
                break;
        }
    }

}
